package com.ruyuan.payment.server.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.common.models.AlipayTradeCloseResponse;
import com.alipay.easysdk.payment.common.models.AlipayTradeQueryResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ruyuan.payment.server.alipay.AliPayService;
import com.ruyuan.payment.server.context.LoginMemberContext;
import com.ruyuan.payment.server.domain.OrderInfo;
import com.ruyuan.payment.server.domain.OrderPay;
import com.ruyuan.payment.server.domain.OrderPayExample;
import com.ruyuan.payment.server.domain.OrderPayRefund;
import com.ruyuan.payment.server.enums.*;
import com.ruyuan.payment.server.exception.BusinessException;
import com.ruyuan.payment.server.exception.BusinessExceptionEnum;
import com.ruyuan.payment.server.mapper.OrderPayMapper;
import com.ruyuan.payment.server.req.OrderPayAddReq;
import com.ruyuan.payment.server.req.OrderPayQueryReq;
import com.ruyuan.payment.server.resp.OrderPayQueryResp;
import com.ruyuan.payment.server.resp.PageResp;
import com.ruyuan.payment.server.util.CopyUtil;
import com.ruyuan.payment.server.util.DateTimeUtil;
import com.ruyuan.payment.server.util.NumUtil;
import com.ruyuan.payment.server.util.SnowFlake;
import com.ruyuan.payment.server.wxpay.WxPayService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

@Service
public class OrderPayService {

    private static final Logger LOG = LoggerFactory.getLogger(OrderPayService.class);

    @Resource
    private OrderPayMapper orderPayMapper;

    @Resource
    private AliPayService aliPayService;

    @Resource
    private WxPayService wxPayService;

    @Resource
    private OrderInfoService orderInfoService;

    @Resource
    private SnowFlake snowFlake;

    @Resource
    private AccountService accountService;

    @Resource
    private PaySuccessService paySuccessService;

    @Resource
    private PayCloseService payCloseService;

    @Resource
    private PayRefundService payRefundService;

    public PageResp<OrderPayQueryResp> queryList(OrderPayQueryReq req) {
        String payNo = req.getPayNo();
        Long memberId = req.getMemberId();
        OrderPayExample orderPayExample = new OrderPayExample();
        OrderPayExample.Criteria criteria = orderPayExample.createCriteria();
        if (!ObjectUtils.isEmpty(payNo)) {
            criteria.andPayNoEqualTo(payNo);
        }
        if (!ObjectUtils.isEmpty(memberId)) {
            criteria.andMemberIdEqualTo(memberId);
        }
        orderPayExample.setOrderByClause("id desc");

        LOG.info("查询页码：{}", req.getPage());
        LOG.info("每页条数：{}", req.getSize());
        PageHelper.startPage(req.getPage(), req.getSize());
        List<OrderPay> orderPayList = orderPayMapper.selectByExample(orderPayExample);

        PageInfo<OrderPay> pageInfo = new PageInfo<>(orderPayList);
        LOG.info("总行数：{}", pageInfo.getTotal());
        LOG.info("总页数：{}", pageInfo.getPages());

        List<OrderPayQueryResp> queryRespList = CopyUtil.copyList(orderPayList, OrderPayQueryResp.class);

        PageResp<OrderPayQueryResp> pageResp = new PageResp<>();
        pageResp.setTotal(pageInfo.getTotal());
        pageResp.setList(queryRespList);

        return pageResp;
    }

    public String pay(OrderPayAddReq req) {
        String channel = req.getChannel();
        BigDecimal accountAmount = req.getAccountAmount();
        BigDecimal channelAmount = req.getChannelAmount();
        BigDecimal amount = accountAmount.add(channelAmount);
        String orderNo = req.getOrderNo();

        // 查询商品订单信息
        OrderInfo orderInfo = orderInfoService.selectByOrderNo(orderNo);

        // 校验
        valid(accountAmount, channelAmount, amount, orderInfo);

        // 保存支付订单
        OrderPay orderPay = saveOrderPay(channel, accountAmount, channelAmount, amount, orderNo);

        if (accountAmount.compareTo(BigDecimal.ZERO) == 0) {
            // 纯渠道支付
            return channelPay(channel, channelAmount, orderInfo, orderPay);
        } else {
            if (accountAmount.compareTo(amount) == 0) {
                // 纯余额支付
                accountService.doAccount(orderInfo.getMemberId(), BigDecimal.ZERO.subtract(accountAmount), "余额支付：" + accountAmount + "元");
                paySuccessService.afterPaySuccess(orderPay.getPayNo(), null);
                return "1";
            } else {
                // 部分余额支付
                // 1 冻结余额
                accountService.freeze(orderInfo.getMemberId(), accountAmount, orderPay.getPayNo());

                // 2 渠道支付
                return channelPay(channel, channelAmount, orderInfo, orderPay);
            }
        }
    }

    private String channelPay(String channel, BigDecimal channelAmount, OrderInfo orderInfo, OrderPay orderPay) {
        if (channel.equals(OrderPayChannelEnum.ALIPAY.getCode())) {
            // 调用支付宝下单接口
            AlipayTradePagePayResponse response = aliPayService.pay(orderInfo.getDesc(), orderPay.getPayNo(), channelAmount.toPlainString());
            return response.getBody();
        } if (channel.equals(OrderPayChannelEnum.WXPAY.getCode())) {
            // 调用微信下单接口
            String codeUrl = wxPayService.pay(orderInfo.getDesc(), orderPay.getPayNo(), channelAmount);
            return codeUrl;
        } else {
            LOG.warn("支付渠道【{}】不存在", channel);
            throw new BusinessException(BusinessExceptionEnum.PAY_ERROR);
        }
    }

    private OrderPay saveOrderPay(String channel, BigDecimal accountAmount, BigDecimal channelAmount, BigDecimal amount, String orderNo) {
        Date now = new Date();
        // OrderPay orderPay = CopyUtil.copy(req, OrderPay.class);
        OrderPay orderPay = new OrderPay();
        orderPay.setId(snowFlake.nextId());
        orderPay.setMemberId(LoginMemberContext.getId());
        orderPay.setOrderNo(orderNo);
        orderPay.setPayNo(NumUtil.genPayNo());
        orderPay.setCashTime(now);
        orderPay.setAmount(amount);
        orderPay.setAccountAmount(accountAmount);
        orderPay.setChannelAmount(channelAmount);
        orderPay.setRefundAmount(BigDecimal.ZERO);
        orderPay.setChannel(channel);
        orderPay.setChannelTime(null);
        orderPay.setStatus(OrderPayStatusEnum.INIT.getCode());
        orderPay.setBillStatus(BillStatusEnum.INIT.getCode());
        orderPay.setCreatedTime(now);
        orderPay.setUpdatedTime(now);
        orderPayMapper.insert(orderPay);
        return orderPay;
    }

    private void valid(BigDecimal accountAmount, BigDecimal channelAmount, BigDecimal amount, OrderInfo orderInfo) {
        // 充值不允许用余额支付
        if (orderInfo.getBusiness().equals(OrderInfoBusinessEnum.DEPOSIT.getCode())
                && accountAmount.compareTo(BigDecimal.ZERO) > 0) {
            LOG.warn("充值不允许用余额支付，余额支付：{}", accountAmount);
            throw new BusinessException(BusinessExceptionEnum.PAY_ERROR);
        }
        // 会员校验
        if (!LoginMemberContext.getId().equals(orderInfo.getMemberId())) {
            LOG.warn("调用支付接口失败，原因：登录会员【{}】与订单会员【{}】不匹配", LoginMemberContext.getId(), orderInfo.getMemberId());
            throw new BusinessException(BusinessExceptionEnum.PAY_ERROR);
        }
        // 状态校验
        if (!orderInfo.getStatus().equals(OrderInfoStatusEnum.INIT.getCode())) {
            LOG.warn("调用支付接口失败，原因：订单状态【{}】非初始", orderInfo.getStatus());
            throw new BusinessException(BusinessExceptionEnum.PAY_ERROR);
        }
        // 金额校验，BigDecimal不能用equal
        if (amount.compareTo(orderInfo.getAmount()) != 0) {
            LOG.warn("调用支付接口失败，原因：支付金额不正确，商品金额【{}】 != 账户支付金额【{}】 + 渠道支付金额【{}】", orderInfo.getAmount(), accountAmount, channelAmount);
            throw new BusinessException(BusinessExceptionEnum.PAY_ERROR);
        }
    }

    public OrderPay selectByPayNo(String payNo) {
        OrderPayExample orderPayExample = new OrderPayExample();
        orderPayExample.createCriteria().andPayNoEqualTo(payNo);
        List<OrderPay> list = orderPayMapper.selectByExample(orderPayExample);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        } else {
            return list.get(0);
        }
    }

    public String queryChannel(String payNo) {
        OrderPay orderPayDB = selectByPayNo(payNo);
        if (ObjectUtils.isEmpty(orderPayDB)) {
            LOG.warn("交易记录不存在，payNo：{}", payNo);
            return "交易记录不存在";
        }
        if (!OrderPayStatusEnum.INIT.getCode().equals(orderPayDB.getStatus())) {
            LOG.warn("支付状态非【未支付】，payNo：{}，status：{}", payNo, orderPayDB.getStatus());
            return "支付状态非【未支付】";
        }

        if (OrderPayChannelEnum.ALIPAY.getCode().equals(orderPayDB.getChannel())) {
            AlipayTradeQueryResponse response = aliPayService.query(payNo);
            String tradeStatus = response.getTradeStatus();
            if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
                String sendPayDate = response.getSendPayDate();
                Date date = DateTimeUtil.parse(sendPayDate, "yyyy-MM-dd HH:mm:ss");
                paySuccessService.afterPaySuccess(payNo, date);
                return "查询支付通道交易成功！";
            } else if ("TRADE_CLOSED".equals(tradeStatus)) {
                return "未付款交易超时关闭，或支付完成后全额退款";
            } else if ("WAIT_BUYER_PAY".equals(tradeStatus)) {
                return "交易创建，等待买家付款";
            } else {
                return "通道返回：" + response.getSubMsg();
            }
        } else if (OrderPayChannelEnum.WXPAY.getCode().equals(orderPayDB.getChannel())) {
            String bodyStr = wxPayService.query(payNo);
            JSONObject responseJson = JSON.parseObject(bodyStr);
            String tradeStatus = responseJson.getString("trade_state");
            if ("SUCCESS".equals(tradeStatus)) {
                Date date = responseJson.getDate("success_time");
                paySuccessService.afterPaySuccess(payNo, date);
                return "查询支付通道交易成功！";
            } else if ("CLOSED".equals(tradeStatus)) {
                return "未付款交易超时关闭，或支付完成后全额退款";
            } else if ("NOTPAY".equals(tradeStatus)) {
                return "交易创建，等待买家付款";
            } else {
                return "通道返回：" + responseJson.getString("trade_state_desc");
            }
        }

        return "";
    }

    public String closeChannel(String payNo) {
        OrderPay orderPayDB = selectByPayNo(payNo);
        return closeChannel(orderPayDB);
    }

    public String closeChannel(OrderPay orderPayDB) {
        String payNo = orderPayDB.getPayNo();
        if (ObjectUtils.isEmpty(orderPayDB)) {
            LOG.warn("交易记录不存在，payNo：{}", payNo);
            return "交易记录不存在";
        }
        if (!OrderPayStatusEnum.INIT.getCode().equals(orderPayDB.getStatus())) {
            LOG.warn("支付状态非【未支付】，payNo：{}，status：{}", payNo, orderPayDB.getStatus());
            return "支付状态非【未支付】";
        }

        if (OrderPayChannelEnum.ALIPAY.getCode().equals(orderPayDB.getChannel())) {
            AlipayTradeCloseResponse response = aliPayService.close(payNo);
            if (ResponseChecker.success(response)) {
                // 执行关闭逻辑
                payCloseService.close(orderPayDB);
                return "关闭订单成功";
            } else if ("ACQ.TRADE_NOT_EXIST".equals(response.getSubCode())) {
                // 执行关闭逻辑
                payCloseService.close(orderPayDB);
                return "关闭订单成功";
            } else {
                return "关闭订单失败，通道返回：" + response.getSubMsg();
            }
        } else if (OrderPayChannelEnum.WXPAY.getCode().equals(orderPayDB.getChannel())) {
            wxPayService.close(payNo);
            // 执行关闭逻辑
            payCloseService.close(orderPayDB);
            return "关闭订单成功";
        }

        return "";
    }

    public List<OrderPay> selectByOrderNo(String orderNo) {
        OrderPayExample orderPayExample = new OrderPayExample();
        orderPayExample.createCriteria().andOrderNoEqualTo(orderNo);
        List<OrderPay> list = orderPayMapper.selectByExample(orderPayExample);
        return list;
    }

    public String refund(String payNo, BigDecimal refundAmount) {
        OrderPay orderPayDB = selectByPayNo(payNo);
        if (ObjectUtils.isEmpty(orderPayDB)) {
            LOG.warn("交易记录不存在，payNo：{}", payNo);
            return "交易记录不存在";
        }
        if (!OrderPayStatusEnum.SUCCESS.getCode().equals(orderPayDB.getStatus())
            && !OrderPayStatusEnum.PART_REFUND.getCode().equals(orderPayDB.getStatus())) {
            LOG.warn("支付状态非【成功】且非【部分退款】，payNo：{}，status：{}", payNo, orderPayDB.getStatus());
            return "支付状态非【成功】且非【部分退款】";
        }

        BigDecimal hadRefundAmount = orderPayDB.getRefundAmount();
        BigDecimal orderAmount = orderPayDB.getAmount();
        BigDecimal oriChannelAmount = orderPayDB.getChannelAmount();
        BigDecimal oriAccountAmount = orderPayDB.getAccountAmount();

        // 校验本次退款金额+已退款金额，不能超过原订单金额
        if (refundAmount.add(hadRefundAmount).compareTo(orderAmount) > 0) {
            LOG.warn("本次退款金额+已退款金额，超过原订单金额，payNo：{}，refundAmount：{}，hadRefundAmount：{}，orderAmount：{}", payNo, refundAmount, hadRefundAmount, orderAmount);
            return "本次退款金额+已退款金额，超过原订单金额";
        }

        // 计算余额和渠道各退多少钱
        // 按场景处理：根据原交易和已退金额来判断本次交易应该退什么
        //         余额抵扣=0，则只退渠道
        //         渠道支付=0，则只退余额
        //         其它，则都有可能
        //             已退金额 + 本次退款金额 <= 余额抵扣，则只退余额
        //             已退金额 >= 余额抵扣，则只退渠道
        //             其它，则退余额 + 退渠道

        BigDecimal accountRefund = BigDecimal.ZERO;
        BigDecimal channelRefund = BigDecimal.ZERO;
        if (oriAccountAmount.compareTo(BigDecimal.ZERO) == 0) {
            LOG.info("余额抵扣=0，则只退渠道");
            accountRefund = BigDecimal.ZERO;
            channelRefund = refundAmount;
        } else if (oriChannelAmount.compareTo(BigDecimal.ZERO) == 0) {
            LOG.info("渠道支付=0，则只退余额");
            accountRefund = refundAmount;
            channelRefund = BigDecimal.ZERO;
        } else {
            if (hadRefundAmount.add(refundAmount).compareTo(oriAccountAmount) <= 0) {
                LOG.info("已退金额 + 本次退款金额 <= 余额抵扣，则只退余额");
                accountRefund = refundAmount;
                channelRefund = BigDecimal.ZERO;
            } else if (hadRefundAmount.compareTo(oriAccountAmount) >= 0) {
                LOG.info("已退金额 >= 余额抵扣，则只退渠道");
                accountRefund = BigDecimal.ZERO;
                channelRefund = refundAmount;
            } else {
                LOG.info("其它，则退余额 + 退渠道");
                // 原交易支付100 = 余额50 + 渠道50<br>
                // 已退40，本次退20，则余额退10，渠道退10<br>
                accountRefund = oriAccountAmount.subtract(hadRefundAmount);
                channelRefund = refundAmount.subtract(accountRefund);
            }
        }

        //退款前置处理：保存退款表，退款状态为I
        OrderPayRefund orderPayRefundDB = payRefundService.beforeRefund(orderPayDB, accountRefund, channelRefund);

        if (channelRefund.compareTo(BigDecimal.ZERO) == 0) {
            // 纯余额退款
            // 执行本地退款逻辑
            payRefundService.refund(orderPayDB, orderPayRefundDB);
            return "退款成功";
        } else {
            if (OrderPayChannelEnum.ALIPAY.getCode().equals(orderPayDB.getChannel())) {
                AlipayTradeRefundResponse response = aliPayService.refundPart(payNo, channelRefund.toPlainString(), orderPayRefundDB.getRefundNo());
                if (response.isSuccess()) {
                    // // 执行本地退款逻辑 放到退款查询流程里
                    // payRefundService.refund(orderPayDB, orderPayRefundDB);
                    return "退款请求成功";
                } else {
                    return "退款请求失败，通道返回：" + response.getSubMsg();
                }
            } else if (OrderPayChannelEnum.WXPAY.getCode().equals(orderPayDB.getChannel())) {
                boolean refundResult = wxPayService.refundPart(payNo, channelRefund, orderPayRefundDB.getRefundNo(), oriChannelAmount);
                if (refundResult) {
                    // // 执行本地退款逻辑 放到退款查询流程里
                    // payRefundService.refund(orderPayDB, orderPayRefundDB);
                    return "退款请求成功";
                } else {
                    return "退款请求失败!!!";
                }
            } else {
                return "退款失败，通道不存在";
            }
        }
    }

    // public static void main(String[] args) {
    //     System.out.println(new BigDecimal("0.1").equals(new BigDecimal("0.10")));
    // }
}
